package com.tzy.mydb.backend.dm.page;/**
 * @description:
 * @Author HP
 * @create 2022/2/26 16:43
 */

import com.tzy.mydb.backend.dm.pageCache.PageCache;
import com.tzy.mydb.backend.utils.Parser;

import java.util.Arrays;

/**
 * @author HP
 * @create 2022-02-26 16:43
 * @desc
 * 普通页:
 * 一个普通页面以一个 2 字节无符号数起始，表示这一页的空闲位置的偏移。剩下的部分都是实际存储的数据。
 * 所以对普通页的管理，基本都是围绕着对 FSO（Free Space Offset）进行的。例如向页面插入数据：
 *  * 普通页结构
 *  * [FreeSpaceOffset] [Data]
 *  * FreeSpaceOffset: 2字节 空闲位置开始偏移
 **/
public class PageX {
    private static final short OF_FREE = 0;
    private static final short OF_DATA = 2;//short占2个字节
    public static final int MAX_FREE_SPACE = PageCache.PAGE_SIZE - OF_DATA;

    /**
     * 向页面插入数据
     *  将 raw 插入 pg 中，返回插入位置
     */
    public static short insert(Page pg, byte[] raw) {
        pg.setDirty(true);
        short offset = getFSO(pg.getData());//得到页空闲数据的offset
        System.arraycopy(raw, 0, pg.getData(), offset, raw.length);//写入数据
        setFSO(pg.getData(), (short)(offset + raw.length));//写入后更新页空闲数据的offset
        return offset;
    }

    /**
     * 设置空闲空间开始的offset
     * @param raw
     * @param ofData
     */
    private static void setFSO(byte[] raw, short ofData) {
        System.arraycopy(Parser.short2Byte(ofData), 0, raw, OF_FREE, OF_DATA);
    }

    /**
     *  获取 pg 的 FSO，即从哪里开始是空闲数据
     * @param pg
     * @return
     */
    public static short getFSO(Page pg) {
        return getFSO(pg.getData());
    }

    private static short getFSO(byte[] raw) {
        return Parser.parseShort(Arrays.copyOfRange(raw, 0, 2));
    }

    /**
     * 获取页面的空闲空间大小
     * @param pg
     * @return
     */
    public static int getFreeSpace(Page pg) {
        return PageCache.PAGE_SIZE - (int)getFSO(pg.getData());
    }

    /**
     * 将 raw 插入 pg 中的 offset 位置，并将 pg 的 offset 设置为较大的 offset
     * 用于在数据库崩溃后重新打开时，恢复例程直接插入数据
     * @param pg
     * @param raw
     * @param offset
     */
    public static void recoverInsert(Page pg, byte[] raw, short offset) {
        pg.setDirty(true);
        System.arraycopy(raw, 0, pg.getData(), offset, raw.length);

        short rawFSO = getFSO(pg.getData());
        if(rawFSO < offset + raw.length) {
            setFSO(pg.getData(), (short)(offset+raw.length));
        }
    }

    /**
     * 将 raw 插入 pg 中的 offset 位置，不更新 update
     * 用于在数据库崩溃后重新打开时，恢复例程直接修改数据
     * @param pg
     * @param raw
     * @param offset
     */
    public static void recoverUpdate(Page pg, byte[] raw, short offset) {
        pg.setDirty(true);
        System.arraycopy(raw, 0, pg.getData(), offset, raw.length);
    }

    public static byte[] initRaw() {
        byte[] raw = new byte[PageCache.PAGE_SIZE];
        setFSO(raw, OF_DATA);
        return raw;
    }

}
